package org.dromara.pay.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.business.service.GoodsOrderService;
import org.dromara.common.core.exception.CallBackException;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.TopUpCoreService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.pay.adapter.PayAdapter;
import org.dromara.pay.config.WxPayProperties;
import org.dromara.pay.constant.TopUpConstant;
import org.dromara.pay.domain.TopUp;
import org.dromara.pay.domain.request.PayParam;
import org.dromara.pay.mapper.TopUpMapper;
import org.dromara.pay.service.BusinessHandleService;
import org.dromara.pay.service.TopUpService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Optional;


/**
 * 充值
 *
 * @author zlj
 * @since 2023/7/31
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TopUpServiceImpl extends ServiceImpl<TopUpMapper, TopUp> implements TopUpService, TopUpCoreService {

    private final TopUpMapper topUpMapper;
    private final BusinessHandleService businessHandle;
    private final List<PayAdapter> list;
    @Resource
    private WxPayProperties wxPayProperties;

    /**
     * 充值接口
     *
     * @return 唤起充值参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject topUp(PayParam payParam) {
        Boolean checkResult = checkAmount(payParam.getAmount());
        if (BooleanUtil.isFalse(checkResult)) {
            throw new ServiceException("金额非法");
        }
        //生成订单
        TopUp topUp = payParam.toTopUp();
        int result = topUpMapper.insert(topUp);
        if (result == 0) {
            throw new ServiceException("操作失败,请重试");
        }
        //拉起支付
        payParam.setOutTradeNo(String.valueOf(topUp.getTopUpId()));
        return this.pay(payParam);
    }

    /**
     * 支付订单处理
     *
     * @param wxNotice 支付通知的明文信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processWxOrders(WxPayOrderNotifyResult wxNotice) {
        try {
            //修改订单状态
            LambdaUpdateWrapper<TopUp> wrapper = new LambdaUpdateWrapper<>();
            wrapper.set(TopUp::getTransactionId, wxNotice.getTransactionId())
                .set(TopUp::getSuccessTime, DateUtil.parse(wxNotice.getTimeEnd(), "yyyyMMddHHmmss"))
                .set(TopUp::getPayerTotal, NumberUtil.div(BigDecimal.valueOf(wxNotice.getTotalFee()), BigDecimal.valueOf(100), 2))
                .set(TopUp::getPayerCurrency, wxNotice.getCashFeeType())
                .set(TopUp::getStatus, TopUpConstant.STATUS_FINISH);
            wrapper.eq(TopUp::getTopUpId, wxNotice.getOutTradeNo())
                .eq(TopUp::getStatus, TopUpConstant.STATUS_DOING);
            int update = topUpMapper.update(new TopUp(), wrapper);
            if (update == 0) {
                log.error(wrapper.getTargetSql());
                throw new ServiceException("更新订单失败");
            }
            //业务处理
            DataPermissionHelper.ignore(() -> businessHandle.businessHandle(this.getById(wxNotice.getOutTradeNo())));
        } catch (ServiceException e) {
            throw new CallBackException(e.getMessage());
        }
    }

    /**
     * 退款订单处理
     *
     * @param reqInfo 退款通知的明文信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processWxRefund(WxPayRefundNotifyResult.ReqInfo reqInfo) {
        //修改订单流水状态
        this.lambdaUpdate().set(TopUp::getStatus, TopUpConstant.STATUS_REFUND)
            .eq(TopUp::getTopUpId, reqInfo.getOutTradeNo())
            .eq(TopUp::getStatus, TopUpConstant.STATUS_FINISH)
            .update();
        //业务处理
        DataPermissionHelper.ignore(() -> businessHandle.businessRefundHandle(this.getById(reqInfo.getOutTradeNo())));
    }

    /**
     * 查询订单是否支付成功
     *
     * @param topUpId 订单id
     * @return true: 支付成功  false: 支付失败
     */
    @Override
    public Boolean getUserIsPay(Long topUpId) {
        TopUp topUp = this.lambdaQuery().select(TopUp::getStatus).eq(TopUp::getTopUpId, topUpId).one();
        String status = Optional.ofNullable(topUp).map(TopUp::getStatus).orElse(TopUpConstant.STATUS_DOING);
        return status.equals(TopUpConstant.STATUS_FINISH);
    }

    /**
     * 充值规则检查
     *
     * @param amount 充值金额
     * @return true: 符合规则   false: 不符合规则
     */
    private Boolean checkAmount(BigDecimal amount) {
//        BigDecimal min = BigDecimalUtil.toBigDecimal(dictService.getDictLabel(TOP_UP_RULE, TOP_UP_RULE_MIN));
//        BigDecimal max = BigDecimalUtil.toBigDecimal(dictService.getDictLabel(TOP_UP_RULE, TOP_UP_RULE_MAX));
//        BigDecimal multiple = BigDecimalUtil.toBigDecimal(dictService.getDictLabel(TOP_UP_RULE, TOP_UP_RULE_MULTIPLE));
//        boolean b = amount.compareTo(max) <= 0 && amount.compareTo(min) >= 0 && amount.remainder(multiple).compareTo(BigDecimal.ZERO) == 0;
        return true;
    }

    /**
     * 支付
     *
     * @param payParam 支付参数
     * @return 唤起支付参数
     */
    public JSONObject pay(PayParam payParam) {
        for (PayAdapter payAdapter : list) {
            if (payAdapter.support(payParam.getPayWay())) {
                return payAdapter.pay(payParam);
            }
        }
        throw new ServiceException("暂不支持此支付方式");
    }

    /**
     * 退款
     *
     * @param topUpId 订单流水id
     * @return true:成功 false:失败
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean refund(Long topUpId) {
        TopUp one = this.lambdaQuery().select(TopUp::getPayWay, TopUp::getPayerTotal).eq(TopUp::getTopUpId, topUpId).one();
        if (ObjectUtil.isNull(one.getPayerTotal())) {
            throw new ServiceException("订单未支付, 无需退款");
        }
        for (PayAdapter payAdapter : list) {
            if (payAdapter.support(one.getPayWay())) {
                boolean refund = payAdapter.refund(topUpId, one.getPayerTotal());
                if (!refund) {
                    throw new ServiceException("退款发起失败");
                }
                return true;
            }
        }
        throw new ServiceException("暂不支持此退款方式");
    }

    /**
     * 查询系统充值订单
     */
    @Override
    public TopUp queryById(Long topUpId) {
        return baseMapper.selectById(topUpId);
    }

    /**
     * 查询系统充值订单列表
     */
    @Override
    public TableDataInfo<TopUp> queryPageList(TopUp bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TopUp> lqw = buildQueryWrapper(bo);
        IPage<TopUp> result = baseMapper.selectPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询系统充值订单列表
     */
    @Override
    public List<TopUp> queryList(TopUp bo) {
        LambdaQueryWrapper<TopUp> lqw = buildQueryWrapper(bo);
        return baseMapper.selectList(lqw);
    }

    private LambdaQueryWrapper<TopUp> buildQueryWrapper(TopUp bo) {
        LambdaQueryWrapper<TopUp> lqw = Wrappers.lambdaQuery();
        lqw.like(ObjectUtil.isNotNull(bo.getTopUpId()), TopUp::getTopUpId, bo.getTopUpId());
        lqw.like(StrUtil.isNotBlank(bo.getTransactionId()), TopUp::getTransactionId, bo.getTransactionId());
        if (ObjectUtil.isNotNull(bo.getSuccessTime())) {
            lqw.between(TopUp::getSuccessTime, bo.getSuccessTime(), DateUtil.endOfDay(bo.getSuccessTime()));
        }
        lqw.eq(ObjectUtil.isNotNull(bo.getCreateBy()), TopUp::getCreateBy, bo.getCreateBy());
        lqw.eq(StrUtil.isNotBlank(bo.getPayWay()), TopUp::getPayWay, bo.getPayWay());
        lqw.eq(StrUtil.isNotBlank(bo.getStatus()), TopUp::getStatus, bo.getStatus());
        lqw.eq(StrUtil.isNotBlank(bo.getOrderType()), TopUp::getOrderType, bo.getOrderType());
        lqw.orderByDesc(TopUp::getCreateTime);
        return lqw;
    }

    /**
     * 新增系统充值订单
     */
    @Override
    public Boolean insertByBo(TopUp bo) {
        TopUp add = MapstructUtils.convert(bo, TopUp.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setTopUpId(add.getTopUpId());
        }
        return flag;
    }

    /**
     * 修改系统充值订单
     */
    @Override
    public Boolean updateByBo(TopUp bo) {
        TopUp update = MapstructUtils.convert(bo, TopUp.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(TopUp entity) {

    }

    /**
     * 批量删除系统充值订单
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {

        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public TopUp getByOrderId(Long orderId) {
        TopUp one = this.lambdaQuery().select(TopUp::getTopUpId, TopUp::getTransactionId)
            .eq(TopUp::getOrderJson, orderId).eq(TopUp::getStatus, TopUpConstant.STATUS_FINISH)
            .eq(TopUp::getCreateBy, LoginHelper.getUserId()).one();
        if (ObjectUtil.isNull(one)) {
            throw new ServiceException("订单不存在或未支付");
        }
        one.setMchId(wxPayProperties.getMchId());
        return one;
    }

    private final GoodsOrderService orderService;

    @Override
    public Boolean refundByOrderId(Long orderId, String orderType) {
        Long userId = orderService.getById(orderId).getCreateBy();
        //通过订单id退鉴定费
        TopUp one = this.lambdaQuery().eq(TopUp::getOrderJson, orderId).eq(TopUp::getStatus, TopUpConstant.STATUS_FINISH)
            .eq(TopUp::getCreateBy, userId).eq(TopUp::getOrderType, orderType).orderByDesc(TopUp::getCreateTime).last("limit 1").one();
        return SpringUtils.getAopProxy(this).refund(one.getTopUpId());
    }
}




